package belf.migrate.engine.job.structure.helper;

import belf.migrate.api.common.DatabaseCompatibility;
import belf.migrate.api.common.DatabaseIdentifier;
import belf.migrate.api.exception.CatalogException;
import belf.migrate.api.exception.DDLException;
import belf.migrate.api.exception.TriggerNotExistException;
import belf.migrate.api.model.QualifiersModel;
import belf.migrate.api.table.schema.Catalog;
import belf.migrate.api.table.schema.TablePath;
import belf.migrate.api.taskconf.JobContext;
import belf.migrate.api.type.DbObjectType;
import belf.migrate.api.type.LogLevelType;
import belf.migrate.api.util.EngineConf;
import belf.migrate.api.util.PostLog;
import belf.migrate.core.exception.BelfRuntimeException;
import belf.migrate.engine.ddl.BaseDDLConvert;
import belf.migrate.engine.ddl.trigger.TriggerConvertFactory;
import belf.migrate.api.job.Job;
import belf.migrate.engine.job.JobResult;
import belf.migrate.engine.job.JobResultItem;
import belf.migrate.api.taskconf.Task;
import belf.migrate.api.util.ExceptionUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.util.Date;
import java.util.List;

import static belf.migrate.api.job.JobCode.*;

@Slf4j
public class CreateTriggerTool extends SqlTransformTool {
    public CreateTriggerTool(Job job) {
        super(job);
    }

    public CreateTriggerTool(Job job, JobContext jobContext) {
        super(job, jobContext);
    }

    @Override
    public JobResult run() {
        PostLog.sendLogModel(
                job.getJobId(),
                job.getTask().getTaskType().getName(),
                "创建触发器开始！",
                LogLevelType.INFO.getType(),
                EXECUTING,
                jobContext.getProgress());
        JobResult jobResult = new JobResult(SUCCESS, "创建选中的触发器成功");
        Catalog sourceCatalog = jobContext.getSourceCatalog();

        try {
            List triggerNames; //待创建的视图名称列表
            // 若限定了要处理的视图名称
            if (job.getMissions() != null && !job.getMissions().isEmpty()) {
                triggerNames = job.getMissions();
            } else {
                triggerNames = sourceCatalog.listTriggerNames(jobContext.getTaskConf().getSourceConf().getDatabaseName(),
                        jobContext.getTaskConf().getSourceConf().getSchemaName());
            }

            if (!CollectionUtils.isEmpty(triggerNames)) {
                transform(triggerNames, jobResult);
            }
        } catch (CatalogException e) {
            e.printStackTrace();
        }

        if (jobResult.getFailtures() > 0) {
            jobResult.setCode(CREATE_TRIGGER_ERROR);
            jobResult.setMessage("创建选中的触发器失败");
            log.error("Create Some Triggers Of {} Failed.", jobContext.getValidSinkDatabase());
        } else {
            log.info("Create All Triggers Of {} Successfully.", jobContext.getValidSinkDatabase());
        }

        PostLog.sendLogModel(
                job.getJobId(),
                job.getTask().getTaskType().getName(),
                "创建触发器结束！",
                LogLevelType.INFO.getType(),
                EXECUTING,
                jobContext.getProgress());
        return jobResult;
    }

    @Override
    protected JobResultItem process(Object object) {
        String triggerName = (String)object;
        Catalog sourceCatalog = jobContext.getSourceCatalog();
        Catalog sinkCatalog = jobContext.getSinkCatalog();
        QualifiersModel sourceQualifiersModel = getSourceQualifiersModel();
        QualifiersModel sinkQualifiersModel = getSinkQualifiersModel();
        String startTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");

        TablePath sourceTriggerPath = TablePath.of(jobContext.getTaskConf().getSourceConf().getDatabaseName(),
                jobContext.getTaskConf().getSourceConf().getSchemaName(),
                triggerName,
                sourceQualifiersModel);
        TablePath sinkTriggerPath = TablePath.of(jobContext.getTaskConf().getSinkConf().getDatabaseName(),
                jobContext.getTaskConf().getSinkConf().getSchemaName(),
                triggerName,
                sinkQualifiersModel);

        JobResultItem jobResultItem = new JobResultItem(triggerName, sinkTriggerPath.getCaseTableName(), SUCCESS, "创建触发器" + triggerName + "成功");
        jobResultItem.setType(DbObjectType.TRIGGER.getTypeName());
        jobResultItem.setStartTime(startTime);

        String sourceDdl = "";
        String promptTempalate = """
                    请将以下%s创建触发器的SQL语句转为%s创建触发器的SQL语句：\n
                    %s\n
                    %s
                    """;
        String srcDbCatalog = DatabaseIdentifier.name(jobContext.getTaskConf().getSourceConf().getCatalogName());
        String targetDbCatalog = DatabaseIdentifier.name(jobContext.getTaskConf().getSinkConf().getCatalogName());
        String requirement = PromtHelper.requirement(jobContext.getTaskConf().getSourceConf().getCatalogName(),
                jobContext.getTaskConf().getSinkConf().getCatalogName(),
                jobContext.getTaskConf().getSinkConf().getSchemaName(),
                getSinkQualifiersModel(),
                DbObjectType.TRIGGER);

        String sinkDDL = null;
        SqlResponse sqlResponse = null;
        try {
            sourceDdl = sourceCatalog.showCreateTrigger(sourceTriggerPath);
            //是否启用数据库的语法兼容性？比如达梦极大兼容Oracle
            boolean dbCompatible = EngineConf.getInstance().getBoolean("llm.enable.db.compatible");
            String prompt = String.format(promptTempalate,
                    srcDbCatalog,
                    dbCompatible? DatabaseCompatibility.name(sinkCatalog.name()):targetDbCatalog,
                    sourceDdl,
                    requirement);
            log.info("转换触发器的提示词：\n{}", prompt);
            sqlResponse = SqlAgent.post(jobContext.getTaskConf().getAiModel(), prompt);
            if (null != sqlResponse) {
                sinkDDL = sqlResponse.getSql();
                jobContext.getSinkCatalog().executeDdl(sinkTriggerPath, sinkDDL);
                jobResultItem.setSinkDdl(sinkDDL);
                log.info("Create Trigger successfully:\n{}", sinkDDL);
            }
        } catch (TriggerNotExistException e) {
            log.error("Trigger create failture: \n{}", sinkDDL);
            String endTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
            PostLog.sendLogModel(
                    job.getJobId(),
                    job.getTask().getTaskType().getName(),
                    "源库" + jobContext.getTaskConf().getSourceConf().getCatalogName() + "触发器不存在：" + triggerName,
                    LogLevelType.ERROR.getType(),
                    EXECUTING,
                    jobContext.getProgress());
            String errorTips = null;
            if (sqlResponse != null) {
                // 返回大模型给出的sql转化理由
                errorTips = String.join("\n", sqlResponse.getReasons());
            }
            return new JobResultItem(triggerName,
                    sinkTriggerPath.getCaseTableName(),
                    CREATE_TRIGGER_ERROR,
                    "源库" + jobContext.getTaskConf().getSourceConf().getCatalogName() + "触发器不存在：" + triggerName,
                    ExceptionUtils.toString(e),
                    DbObjectType.TRIGGER.getTypeName(),
                    startTime,
                    endTime,
                    sourceDdl,
                    sinkDDL,
                    errorTips);
        } catch (DDLException e) {
            log.error("Trigger create failture: \n{}", sinkDDL);
            String endTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS");
            PostLog.sendLogModel(
                    job.getJobId(),
                    job.getTask().getTaskType().getName(),
                    String.format("创建触发器 %s 失败, SQL错误：%s", triggerName, e.getCause().getMessage()),
                    LogLevelType.ERROR.getType(),
                    EXECUTING,
                    jobContext.getProgress());
            String errorTips = null;
            if (sqlResponse != null) {
                // 返回大模型给出的sql转化理由
                errorTips = String.join("\n", sqlResponse.getReasons());
            }
            return new JobResultItem(triggerName,
                    sinkTriggerPath.getCaseTableName(),
                    CREATE_TRIGGER_ERROR,
                    String.format("创建触发器 %s 失败, SQL错误：%s", triggerName, e.getCause().getMessage()),
                    ExceptionUtils.toString(e.getCause()),
                    DbObjectType.TRIGGER.getTypeName(),
                    startTime,
                    endTime,
                    sourceDdl,
                    sinkDDL,
                    errorTips);
        }

        PostLog.sendLogModel(
                job.getJobId(),
                job.getTask().getTaskType().getName(),
                String.format("创建触发器 %s 成功", triggerName),
                LogLevelType.INFO.getType(),
                EXECUTING,
                jobContext.getProgress());
        jobResultItem.setEndTime(DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss.SSS"));
        return jobResultItem;
    }
}
